package bus_srv

import (
	"bytes"
	"context"
	"fmt"
	"github.com/patrickmn/go-cache"
	"github.com/wutianze/nats.go"
	"gluenet/pkg/apis"
	"gluenet/pkg/log/v1"
	"gluenet/pkg/meta"
	"gluenet/pkg/nets"
	"gluenet/pkg/ops/dbi"
	v2 "gluenet/utils/rpc/v2/define"
	"time"
)

type BusServices struct {
	ctx    context.Context
	logger v1.Logger
	DB     dbi.GlueDB

	agentBeats *cache.Cache
	fs         map[string]*nats.Subscription
}

func NewServices(ctx context.Context) *BusServices {
	return &BusServices{
		ctx:        ctx,
		agentBeats: cache.New(time.Second*5, time.Second*5),
		fs:         make(map[string]*nats.Subscription),
	}
}

// Initialize if failed, please logger Fatal
func (r *BusServices) Initialize(logger v1.Logger, ctx *apis.GlueContext) {
	r.logger = logger
	//ctx.DataCache = r.agentBeats

	// init load the agents to listen heartbeat
	var agents []meta.Agent
	if err := ctx.DataBase.AgentListAll(&agents); err != nil {
		logger.Fatalf("init beart listen failed %v", err)
	}
	for _, v := range agents {
		//r.logger.Debugf("guid %v add to heartbead", v.Guid)
		r.agentBeats.Add(v.Guid, v2.Beat{Type: v2.BeatTypeAgent, Status: v.Status}, time.Second*5)
	}

	// init the heatbeat expel func
	r.agentBeats.OnEvicted(func(guid string, beat interface{}) {
		if _, err := ctx.DataBase.MetaDB().Exec(`UPDATE agent_status SET status=? WHERE guid = ?`, v2.AgentStateUnKnow, guid); err != nil {
			r.logger.Errorf("beat stop update %v", err)
		}
		//r.logger.Debugf("guid %v is exited", guid)
	})

	r.register(ctx)
}

func (r *BusServices) Close() error {
	errs := new(bytes.Buffer)
	for k, v := range r.fs {
		if err := v.Unsubscribe(); err != nil {
			errs.WriteString(fmt.Sprintf("unregister %v %v\n", k, err))
		}
	}

	if errs.Len() <= 0 {
		return nil
	} else {
		return fmt.Errorf("%v", errs.String())
	}
}

func (r *BusServices) register(ctx *apis.GlueContext) {
	for _, ot := range routers {
		topic, fun := ot.topic, ot.handler
		r.logger.Debugf("register bus service %v %v", topic, fun)
		if c, err := ctx.Rpc.Subscribe(topic, func(m nets.NetContext) {
			gCtx := *ctx
			gCtx.Logger = r.logger
			gCtx.RpcContext = &m
			fun(&gCtx)
		}); err != nil {
			r.logger.Fatalf("register bus service %v", topic)
			_ = c.Unsubscribe()
		} else {
			r.fs[topic] = c
		}
	}
}
